# -*- coding: utf-8 -*-
from difflib import SequenceMatcher
from general.mathoperations import bm25, normalized_google_distance
from general.stringoperations import ext_full_abbr_info

class Entity():
    def __init__(self, **entity_info):
        self.entity_id = entity_info['entity_id']
        self.entity_title = entity_info['entity_title']
        self.name = self.entity_title
        self.disamb = ""
        temp_title_info = self.entity_title.split("_(")
        if len(temp_title_info) > 1:
            self.name = temp_title_info[0]
            self.disamb = temp_title_info[1][:-1]
        self.inlink_list = []
        self.infreq_dict = {}
        self.second_inlink_list = [0]
        self.outlink_list = []
        self.outfreq_dict = {}
        self.category_list = []
        self.fl = 0
        self.tf_dict = {}
        self.word_dict = {}

    def __str__(self):
        return "Entity_id:{0}\nEntity_title:{1}".format(self.entity_id, self.entity_title)

    def get_tf_dict(self, mysql):
        self.tf_dict = mysql.get_tf_dict(self.entity_id)

class CandidateEntity(Entity):
    def __init__(self, **entity_info):
        Entity.__init__(self, **entity_info)
        self.prior = entity_info['prior']
        self.edit_distance = entity_info['edit_distance']
        self.issub = entity_info['issub']
        self.isabbr = 0
        self.min_i = 0.0
        self.avg_i = 0.0
        self.max_i = 0.0
        self.weight = entity_info['prior']
        self.local_inlink_weight = 0.0
        self.local_outlink_weight = 0.0
        self.local_category_weight = 0.0
        self.bm25 = 0.0
        self.global_inlink_weight = 0.0
        self.global_outlink_weight = 0.0
        self.global_category_weight = 0.0
        self.inlink_google_distance = []

    def __str__(self):
        return "\t\t{0:<20}:{1}\n\t\t{2:<20}:{3}\n\t\t{4:<20}:{5:f}\n".\
                format('Entity_ID', self.entity_id, 'Entity_Title', self.entity_title, \
                'Prior_Probability', self.prior) + \
                "\t\t{0:<20}:{1:f}\n\t\t{2:<20}:{3}\n".\
                format('Edit_Distance', self.edit_distance, 'IsSub', self.issub) + \
                "\t\t{0:<20}:{1}\n\t\t{2:<20}:{3:f}\n\t\t{4:<20}:{5:f}\n\t\t{6:<20}:{7:f}\n".\
                format('IsAbbr', self.isabbr, 'Compress_Rate', self.avg_i, 'Min_I', self.min_i, 'Max_I', self.max_i) +\
                "\t\t{0:<20}:{1:f}\n\t\t{2:<20}:{3:f}\n".\
                format("In_Concept:", self.local_inlink_weight, "Out_Concept:", self.local_outlink_weight) + \
                "\t\t{0:<20}:{1:f}\n".\
                format("Category:", self.local_category_weight) + \
                "\t\t{0:<20}:{1:f}\n\t\t{2:<20}:{3:f}\n".\
                format("Global_In_Concept:", self.global_inlink_weight, "Global_Out_Concept:", self.global_outlink_weight) + \
                "\t\t{0:<20}:{1:f}".\
                format("Global_Category:", self.global_category_weight)
                #"\t\t{0:<20}:{1:f}\n\t\t{2:<20}:{3:f}\n".\
                #format("Min_GD:", self.inlink_google_distance[0], "Avg_GD:", self.inlink_google_distance[1]) + \
                #"\t\t{0:<20}:{1:f}".\
                #format("Max_GD:", self.inlink_google_distance[2])

    def calculate_local(self, term):
        self.string_match(term.name_nospace)
        self.isabbr, self.min_i, self.avg_i, self.max_i = ext_full_abbr_info(self.name, term.name_nospace)

    def string_match(self, mention):
        self.edit_distance = SequenceMatcher(None, self.entity_title, mention).ratio()

        if self.entity_title.find(mention) != -1 or mention.find(self.entity_title) != -1:
            self.issub = 1

    def calculate_local_concept_weight(self, inlink_v, outlink_v):
        for inlink in self.inlink_list:
            #if inlink in inlink_v:
            self.local_inlink_weight += self.infreq_dict[inlink] * inlink_v[inlink]
        for outlink in self.outlink_list:
            #if outlink in outlink_v:
            self.local_outlink_weight += self.outfreq_dict[outlink] * outlink_v[outlink]

    def calculate_local_category_weight(self, category_v):
        for category_id in self.category_list:
            #if category_id in category_v:
            self.local_category_weight += category_v[category_id]

    def calculate_bm25(self, context):
        for word in context:
            if word in self.word_dict:
                times = context[word]
                word_id = self.word_dict[word][0]
                idf = self.word_dict[word][1]
                tf = self.tf_dict[word_id]
                self.bm25 += times * bm25(idf, tf, self.fl)

    def calculate_global_concept_weight(self, inlink_v, outlink_v):
        if inlink_v:
            for inlink in self.inlink_list:
                if inlink in inlink_v:
                    self.global_inlink_weight += self.infreq_dict[inlink] * inlink_v[inlink]
        if outlink_v:
            for outlink in self.outlink_list:
                if outlink in outlink_v:
                    self.global_outlink_weight += self.outfreq_dict[outlink] * outlink_v[outlink]

    def calculate_global_category_weight(self, category_v):
        if category_v:
            for category_id in self.category_list:
                if category_id in category_v:
                    self.global_category_weight += category_v[category_id]

    def calculate_first_order_google_distance(self, term):
        gd = normalized_google_distance(term.inlink_list, self.inlink_list)
        if gd == -1:
            return -1
        else:
            self.inlink_google_distance.append(gd)

    def calculate_second_order_google_distance(self, term):
        div1 = (len(term.second_inlink_list) + len(self.second_inlink_list))
        div2 = (len(term.inlink_list) + len(self.inlink_list))
        if div1 == 0:
            print('Error:\n', term.name, '\n', self.name)
            self.inlink_google_distance.append(1.0)
            return
        div = div1 / div2
        gd = normalized_google_distance(term.second_inlink_list, self.second_inlink_list, div)
        if gd == -1:
            self.inlink_google_distance.append(1.0)
        else:
            self.inlink_google_distance.append(gd)

    def update_google_distance(self):
        if self.inlink_google_distance == []:
            self.inlink_google_distance = [1.0]
        min_g = min(self.inlink_google_distance)
        max_g = max(self.inlink_google_distance)
        mean_g = sum(self.inlink_google_distance) / len(self.inlink_google_distance)
        self.inlink_google_distance = [min_g, mean_g, max_g]

